{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import division  \n",
    "import numpy as np  \n",
    "import scipy as sp  \n",
    "class  Item_based_CF:  \n",
    "    def __init__(self, X):  \n",
    "        self.X = X  #评分表\n",
    "        self.mu = np.mean(self.X[:,2])  #average rating\n",
    "\n",
    "        self.ItemsForUser={}   #用户打过分的所有Item\n",
    "        self.UsersForItem={}   #给Item打过分的所有用户\n",
    "        \n",
    "        for i in range(self.X.shape[0]):  \n",
    "            uid=self.X[i][0]  #user id\n",
    "            i_id=self.X[i][1] #item_id \n",
    "            rat=self.X[i][2]  #rating\n",
    "            \n",
    "            self.UsersForItem.setdefault(i_id,{})  \n",
    "            self.ItemsForUser.setdefault(uid,{}) \n",
    "            \n",
    "            self.UsersForItem[i_id][uid]=rat  \n",
    "            self.ItemsForUser[uid][i_id]=rat\n",
    "            \n",
    "            #self.similarity.setdefault(i_id,{}) \n",
    "            \n",
    "        pass  \n",
    "    \n",
    "        n_Items = len(self.UsersForItem)+1 #数组的索引从0开始，浪费第0个元素\n",
    "        print (n_Items-1)\n",
    "        self.similarity = np.zeros((n_Items, n_Items), dtype=np.float)\n",
    "        self.similarity[:,:] = -1\n",
    "           \n",
    "    \n",
    "    #计算Item i_id1和i_id2之间的相似性\n",
    "    def sim_cal(self, i_id1, i_id2):\n",
    "        if self.similarity[i_id1][i_id2]!=-1:  #如果已经计算好\n",
    "            return self.similarity[i_id1][i_id2]  \n",
    "        \n",
    "        si={}  \n",
    "        for user in self.UsersForItem[i_id1]:  #所有对Item1打过分的的user\n",
    "            if user in self.UsersForItem[i_id2]:  #如果该用户对Item2也打过分\n",
    "                #print self.UsersForItem[i_id2]\n",
    "                si[user]=1  #user为一个有效用用户\n",
    "        \n",
    "        #print si\n",
    "        n=len(si)   #有效用户数，有效用户为即对Item1打过分，也对Item2打过分\n",
    "        if (n==0):  #没有共同打过分的用户，相似度设为1.因为最低打分为1？\n",
    "            self.similarity[i_id1][i_id2]=0  \n",
    "            self.similarity[i_id1][i_id1]=0  \n",
    "            return 0  \n",
    "        \n",
    "        #所有有效用户对Item1的打分\n",
    "        s1=np.array([self.UsersForItem[i_id1][u] for u in si])  \n",
    "        \n",
    "        #所有有效用户对Item2的打分\n",
    "        s2=np.array([self.UsersForItem[i_id2][u] for u in si])  \n",
    "        \n",
    "        sum1=np.sum(s1)  \n",
    "        sum2=np.sum(s2)  \n",
    "        sum1Sq=np.sum(s1**2)  \n",
    "        sum2Sq=np.sum(s2**2)  \n",
    "        pSum=np.sum(s1*s2)  \n",
    "        \n",
    "        #分子\n",
    "        num=pSum-(sum1*sum2/n)  \n",
    "        \n",
    "        #分母\n",
    "        den=np.sqrt((sum1Sq-sum1**2/n)*(sum2Sq-sum2**2/n))  \n",
    "        if den==0:  \n",
    "            self.similarity[i_id1][i_id2]=0  \n",
    "            self.similarity[i_id2][i_id1]=0  \n",
    "            return 0  \n",
    "        \n",
    "        self.similarity[i_id1][i_id2]=num/den  \n",
    "        self.similarity[i_id2][i_id1]=num/den  \n",
    "        return num/den  \n",
    "            \n",
    "    #预测用户uid对Item i_id的打分\n",
    "    def pred(self,uid,i_id):  \n",
    "        sim_accumulate=0.0  \n",
    "        rat_acc=0.0  \n",
    "        \n",
    "        if(i_id == 599):    \n",
    "            print (self.UsersForItem[i_id])\n",
    "            \n",
    "        for item in self.ItemsForUser[uid]:  #用户uid打过分的所有Item\n",
    "            sim = self.sim_cal(item,i_id)    #该Item与i_id之间的相似度\n",
    "            if sim<0:continue  \n",
    "            #print sim,self.user_movie[uid][item],sim*self.user_movie[uid][item]  \n",
    "            \n",
    "            rat_acc += sim * self.ItemsForUser[uid][item]  \n",
    "            sim_accumulate += sim  \n",
    "        \n",
    "        #print rat_acc,sim_accumulate  \n",
    "        if sim_accumulate==0: #no same user rated,return average rates of the data  \n",
    "            return  self.mu  \n",
    "        return rat_acc/sim_accumulate  \n",
    "    \n",
    "    #测试\n",
    "    def test(self,test_X):  \n",
    "        test_X=np.array(test_X) \n",
    "        output=[]  \n",
    "        sums=0  \n",
    "        print (\"the test data size is \",test_X.shape)  \n",
    "        for i in range(test_X.shape[0]):  \n",
    "            uid = test_X[i][0]  #user id\n",
    "            i_id = test_X[i][1] #item_id \n",
    "        \n",
    "            #设置默认值，否则用户或item没在训练集中出现时会报错\n",
    "            self.UsersForItem.setdefault(i_id,{})  \n",
    "            self.ItemsForUser.setdefault(uid,{})\n",
    "            \n",
    "            pre=self.pred(uid, i_id)  \n",
    "            output.append(pre)  \n",
    "            #print pre,test_X[i][2]  \n",
    "            sums += (pre-test_X[i][2])**2  \n",
    "        rmse=np.sqrt(sums/test_X.shape[0])  \n",
    "        print (\"the rmse on test data is \",rmse  )\n",
    "        return output  "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
